﻿// 忽略 安全警告
#define _CRT_SECURE_NO_WARNINGS 1

#include "FileHandler.h"
#include "BorrowingRecord.h"
#include <iostream>
#include <vector>

/**
 * @brief 构造函数
 * @param filePath 文件路径
 */
FileHandler::FileHandler(const std::filesystem::path &booksPath, const std::filesystem::path &recordsPath) {
    booksFilePath = booksPath;
    recordsFilePath = recordsPath;
}

/**
 * @brief 读取数据
 * @param books 图书数据
 * @param borrowingRecords 借阅记录
 */
void FileHandler::loadData(std::vector<Book> &books, std::vector<BorrowingRecord> &borrowingRecords) {

    // 二进制方式打开文件

    std::ifstream ifs(booksFilePath, std::ios::binary);
    std::ifstream ifs2(recordsFilePath, std::ios::binary);

    if (!ifs.is_open() || !ifs2.is_open()) {
        if (!ifs.is_open()) {
            std::cout << "图书数据文件不存在，将为你新建数据文件！\n";
            std::ofstream ofs(booksFilePath, std::ios::binary | std::ios::app);// 以二进制方式打开文件，如果文件不存在则创建文件
            if (!ofs.is_open()) {
                throw std::runtime_error("图书数据文件创建失败，请检查文件权限！\n");        // 抛出异常
            }
            ofs.close();
        }
        if (!ifs2.is_open()) {
            std::cout << "借阅记录文件不存在，将为你新建数据文件！\n";
            std::ofstream ofs2(recordsFilePath, std::ios::binary | std::ios::app);// 以二进制方式打开文件，如果文件不存在则创建文件
            if (!ofs2.is_open()) {
                throw std::runtime_error("借阅记录文件创建失败，请检查文件权限！\n");        // 抛出异常
            }
            ofs2.close();
        }

        // 重新打开文件
        ifs.open(booksFilePath, std::ios::binary);
        ifs2.open(recordsFilePath, std::ios::binary);
        if (!ifs.is_open()) {
            throw std::runtime_error("无法打开图书数据文件！\n");
        }
        if (!ifs2.is_open()) {
            throw std::runtime_error("无法打开借阅记录文件！\n");
        }
    } else {
        int bookNum;
        ifs.read(reinterpret_cast<char *>(&bookNum), sizeof(int));
        for (int i = 0; i < bookNum; i++) {
            std::string name, author, ISBN, publisher, publishDate, barcode, location;
            double price;
            int classification, total, available;
            readString(ifs, name);
            readString(ifs, author);
            readString(ifs, ISBN);
            ifs.read(reinterpret_cast<char *>(&price), sizeof(double));
            readString(ifs, publisher);
            readString(ifs, publishDate);
            readString(ifs, barcode);
            ifs.read(reinterpret_cast<char *>(&classification), sizeof(int));
            readString(ifs, location);
            ifs.read(reinterpret_cast<char *>(&total), sizeof(int));
            ifs.read(reinterpret_cast<char *>(&available), sizeof(int));
            Book book(name, author, ISBN, price, publisher, publishDate, barcode, classification, location, total,
                      available);
            book.setIndex(i + 1);       // 设置索引
            books.push_back(book);
        }
        std::cout << "图书数据读取成功！" << std::endl;

        auto getDaysInYear = [](int year) {
            if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
                return 366; // 闰年有 366 天
            } else {
                return 365; // 平年有 365 天
            }
        };

        auto getDaysInMonth = [&getDaysInYear](int year, int month) {
            static const int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
            if (month == 2 && getDaysInYear(year) == 366) {
                return 29; // 闰年的 2 月有 29 天
            } else {
                return daysInMonth[month - 1];
            }
        };

        int recordNum;
        ifs2.read(reinterpret_cast<char *>(&recordNum), sizeof(int));
        for (int i = 0; i < recordNum; i++) {
            std::string ID, bookISBN, readerID, borrowDate, returnDate, continueDate, status;
            int remainingDays = 0, index;
            readString(ifs2, ID);
            readString(ifs2, bookISBN);
            readString(ifs2, readerID);
            readString(ifs2, borrowDate);
            readString(ifs2, returnDate);
            readString(ifs2, continueDate);
            readString(ifs2, status);

            index = i + 1;      // 设置索引
            // 如果书籍已归还，则设置状态为已归还，并不再计算剩余天数
            if (!(status == "已归还")) {
                // 计算剩余天数，获取当前日期，如果续借日期为空，则剩余天数为借书日期到今天的天数，否则剩余天数为续借日期到今天的天数
                // 如果剩余天数小于 0，则设置状态为逾期
                std::chrono::system_clock::time_point today = std::chrono::system_clock::now();
                std::time_t tt = std::chrono::system_clock::to_time_t(today);
                std::tm *now = std::localtime(&tt);
                int year = now->tm_year + 1900;
                int month = now->tm_mon + 1;
                int day = now->tm_mday;

                if (continueDate.empty()) {
                    int returnYear = std::stoi(returnDate.substr(0, 4));
                    int returnMonth = std::stoi(returnDate.substr(5, 2));
                    int returnDay = std::stoi(returnDate.substr(8, 2));

                    while (returnYear < year || (returnYear == year && returnMonth < month) ||
                           (returnYear == year && returnMonth == month && returnDay < day)) {
                        remainingDays++;
                        returnDay++;

                        if (returnDay > getDaysInMonth(returnYear, returnMonth)) {
                            returnDay = 1;
                            returnMonth++;
                            if (returnMonth > 12) {
                                returnMonth = 1;
                                returnYear++;
                            }
                        }
                    }
                } else {
                    int continueYear = std::stoi(continueDate.substr(0, 4));
                    int continueMonth = std::stoi(continueDate.substr(5, 2));
                    int continueDay = std::stoi(continueDate.substr(8, 2));

                    while (continueYear < year || (continueYear == year && continueMonth < month) ||
                           (continueYear == year && continueMonth == month && continueDay < day)) {
                        remainingDays++;
                        continueDay++;

                        if (continueDay > getDaysInMonth(continueYear, continueMonth)) {
                            continueDay = 1;
                            continueMonth++;
                            if (continueMonth > 12) {
                                continueMonth = 1;
                                continueYear++;
                            }
                        }
                    }
                }

                if (remainingDays < 0) {
                    status = "逾期";
                } else if (remainingDays == 0) {
                    status = "今日到期";
                } else {
                    status = "未归还";
                }
            }
            BorrowingRecord borrowingRecord(ID, bookISBN, readerID, borrowDate, returnDate, continueDate, status,
                                            remainingDays, index);
            borrowingRecords.push_back(borrowingRecord);
        }
        std::cout << "借阅记录读取成功！" << std::endl;
    }
    ifs.close();
    ifs2.close();
}

/**
 * @brief 写入数据
 * @param books 图书数据
 * @param borrowingRecords 借阅记录
 */
void FileHandler::saveData(std::vector<Book> &books, std::vector<BorrowingRecord> &borrowingRecords) {
    // 打开文件
    std::ofstream ofs(booksFilePath, std::ios::binary);
    std::ofstream ofs2(recordsFilePath, std::ios::binary);

    if (!ofs.is_open() || !ofs2.is_open()) {
        throw std::runtime_error("无法打开文件！\n");
    }

    // 写入数据
    int bookNum = (int) books.size();
    ofs.write(reinterpret_cast<char *>(&bookNum), sizeof(int));
    for (auto &book: books) {
        writeString(ofs, book.getName());
        writeString(ofs, book.getAuthor());
        writeString(ofs, book.getISBN());
        double price = book.getPrice();
        ofs.write(reinterpret_cast<const char *>(&price), sizeof(double));
        writeString(ofs, book.getPublisher());
        writeString(ofs, book.getPublishDate());
        writeString(ofs, book.getBarcode());
        int classification = book.getClassification();
        ofs.write(reinterpret_cast<const char *>(&classification), sizeof(int));
        writeString(ofs, book.getLocation());
        int total = book.getTotal();
        ofs.write(reinterpret_cast<const char *>(&total), sizeof(int));
        int available = book.getAvailable();
        ofs.write(reinterpret_cast<const char *>(&available), sizeof(int));
    }
    std::cout << "图书数据写入成功！" << std::endl;

    int recordNum = (int) borrowingRecords.size();
    ofs2.write(reinterpret_cast<char *>(&recordNum), sizeof(int));
    for (auto &borrowingRecord: borrowingRecords) {
        writeString(ofs2, borrowingRecord.getID());
        writeString(ofs2, borrowingRecord.getBookISBN());
        writeString(ofs2, borrowingRecord.getBorrowerID());
        writeString(ofs2, borrowingRecord.getBorrowDate());
        writeString(ofs2, borrowingRecord.getReturnDate());
        writeString(ofs2, borrowingRecord.getContinueDate());
        writeString(ofs2, borrowingRecord.getStatus());
    }
    std::cout << "借阅记录写入成功！" << std::endl;

    ofs.close();
    ofs2.close();
}

// 读取字符串
void FileHandler::readString(std::ifstream &ifs, std::string &str) {
    std::size_t size;
    ifs.read(reinterpret_cast<char *>(&size), sizeof(std::size_t));
    str.resize(size);
    ifs.read(str.data(), size);
}

// 写入字符串
void FileHandler::writeString(std::ofstream &ofs, const std::string &str) {
    std::size_t size = str.size();
    ofs.write(reinterpret_cast<const char *>(&size), sizeof(std::size_t));
    ofs.write(str.data(), size);
}

